﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace DARE
{
    public class CTerrainModel
    {

    #region fields

        private const int m_cMaxSize = 128;
        private const int m_cDefaultSubSize = 16;

        private CNode m_rootNode;
        private int m_subSizeX;
        private int m_subSizeZ;
        private CTerrainInfo m_terrainInfo;

    #endregion

    #region properties

        public CNode RootNode
        {
            get { return m_rootNode; }
        }

        public CTerrainInfo TerrainInfo
        {
            get { return m_terrainInfo; }
        }

        public int SubSizeX
        {
            get { return m_subSizeX; }
        }

        public int SubSizeZ
        {
            get { return m_subSizeZ; }
        }

    #endregion

    #region ctor

        public CTerrainModel(CTerrainInfo terrainInfo)
        {
            Initialize(terrainInfo, m_cDefaultSubSize, m_cDefaultSubSize);
        }

        public CTerrainModel(CTerrainInfo terrainInfo, int subSize)
        {
            Initialize(terrainInfo, subSize, subSize);
        }

        public CTerrainModel(CTerrainInfo terrainInfo, int subSizeX, int subSizeZ)
        {
            Initialize(terrainInfo, subSizeX, subSizeZ);
        }

        private void Initialize(CTerrainInfo terrainInfo, int subSizeX, int subSizeZ)
        {
            if (terrainInfo == null)
                throw new Exception("CTerrainModel: CTerrainInfo cannot be null !");
            m_terrainInfo = terrainInfo;
            m_subSizeX = subSizeX;
            m_subSizeZ = subSizeZ;
            m_rootNode = new CNode();
            Setup();
        }

    #endregion

    #region methods

        public void SetTexture(Texture2D texture)
        {
            SetTexture(texture, Vector2.One, Vector2.Zero);
        }

        public void SetTexture(Texture2D texture, Vector2 tile)
        {
            SetTexture(texture, tile, Vector2.Zero);
        }

        public void SetTexture(Texture2D texture, Vector2 tile, Vector2 offset)
        {
            foreach (CNode child in m_rootNode.Children.Values)
            {
                CSubTerrainModel hm = child.Entity.Model as CSubTerrainModel;

                hm.Effect.IsMultiTexturingActive = false;

                hm.Effect.ColorMap = texture;
                hm.Effect.ColorMapTiles = tile;
                hm.Effect.ColorMapOffset = offset;
            }
        }

        public void SetTexture(Texture2D multi, 
            Texture2D texture0, Texture2D texture1, Texture2D texture2, Texture2D texture3)
        {
            SetTexture(multi, 
                texture0, Vector2.One, Vector2.Zero,
                texture1, Vector2.One, Vector2.Zero,
                texture2, Vector2.One, Vector2.Zero,
                texture3, Vector2.One, Vector2.Zero);
        }

        public void SetTexture(Texture2D multi, 
            Texture2D texture0, Vector2 tile0,
            Texture2D texture1, Vector2 tile1,
            Texture2D texture2, Vector2 tile2,
            Texture2D texture3, Vector2 tile3)
        {
            SetTexture(multi, 
                texture0, tile0, Vector2.Zero,
                texture1, tile1, Vector2.Zero,
                texture2, tile2, Vector2.Zero,
                texture3, tile3, Vector2.Zero);
        }

        public void SetTexture(Texture2D multi, 
            Texture2D texture0, Vector2 tile0, Vector2 offset0, 
            Texture2D texture1, Vector2 tile1, Vector2 offset1, 
            Texture2D texture2, Vector2 tile2, Vector2 offset2, 
            Texture2D texture3, Vector2 tile3, Vector2 offset3)
        {
            foreach (CNode child in m_rootNode.Children.Values)
            {
                CSubTerrainModel hm = child.Entity.Model as CSubTerrainModel;

                hm.Effect.IsMultiTexturingActive = true;

                hm.Effect.MultiTexturingMap = multi;

                hm.Effect.ColorMap = texture0;
                hm.Effect.ColorMapTiles = tile0;
                hm.Effect.ColorMapOffset = offset0;

                hm.Effect.ColorMap1 = texture1;
                hm.Effect.ColorMapTiles1 = tile1;
                hm.Effect.ColorMapOffset1 = offset1;

                hm.Effect.ColorMap2 = texture2;
                hm.Effect.ColorMapTiles2 = tile2;
                hm.Effect.ColorMapOffset2 = offset2;

                hm.Effect.ColorMap3 = texture3;
                hm.Effect.ColorMapTiles3 = tile3;
                hm.Effect.ColorMapOffset3 = offset3;
            }
        }

        private void Setup()
        {
            CNode child;
            CSubTerrainModel subTerrainModel;
            int subX, subZ;

            for (int z = 0; z < m_terrainInfo.Heightmap.Height; z += m_subSizeZ)
                for (int x = 0; x < m_terrainInfo.Heightmap.Width; x += m_subSizeX)
                {
                    child = m_rootNode.CreateChild().SetPosition(
                        (x - m_terrainInfo.Heightmap.Width / 2 + m_subSizeX / 2) * m_terrainInfo.Scale, 0,
                        (z - m_terrainInfo.Heightmap.Height / 2 + m_subSizeZ / 2) * m_terrainInfo.Scale, CNode.ESpace.WORLD);

                    subX = x < m_terrainInfo.Heightmap.Width - m_subSizeX ? m_subSizeX + 1 : m_subSizeX;
                    subZ = z < m_terrainInfo.Heightmap.Height - m_subSizeZ ? m_subSizeZ + 1 : m_subSizeZ;

                    subTerrainModel = new CSubTerrainModel(this, x, x + subX, z, z + subZ);

                    child.Entity.AttachModel(subTerrainModel);
                    child.AABB = subTerrainModel.WorldBoundingBox;
                }
        }

        // may be simplier, just count the number of "1"
        public static bool IsPowerOfTwo(int nb)
        {
            int i = 0;

            while ((((nb >> i++) & 1) != 1) && (i < 32)) ;
            return i > 31 ? false : ((nb >> i) == 0) ? true : false;
        }

    #endregion

    }
}
